package com.lagou.edu.utils;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotate.Autowired;
import com.lagou.edu.annotate.Service;
import com.lagou.edu.annotate.Transactional;
import com.lagou.edu.factory.ProxyFactory;

import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 扫描项目中所有注解
 */
public class ScanContext {
    //包名
    private String packageName;
    //存放Bean的对象的map
    private ConcurrentHashMap<String, Object> beans = new ConcurrentHashMap<>();

    public ScanContext(String packageName) {
        this.packageName = packageName;
        //开始初始化bean对象
        initBeans();
    }


    /**
     * 初始化Bean对象信息
     */
    private void initBeans() {
        //获取包下面的所有Class类集合
        List<Class> classList = getClasses(packageName);
        //根据Class类集合初始化每一个Bean对象，并放入到map集合中
        findClassIsAddedCostomAnnotation(classList);
    }

    private List<Class> getClasses(String packageName) {
        //存放根据包路径获取到的Class对象
        List<Class> classList = new ArrayList<>();
        //获取包的文件路径
        String packageDirName = packageName.replace(".", "/");
        //存放获取的资源集合信息
        Enumeration<URL> dirs;
        try {
            //根据包的文件路径加载资源集合信息
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {  //判断是否包含元素信息
                URL url = dirs.nextElement();   //获取元素资源信息
                String protocol = url.getProtocol();  //获取元素资源所属协议
                if (protocol.equals("file")) {   //file 协议
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");  //获取资源路径
                    findAndAddClassesInPackageByFile(packageName, filePath, classList);  //根据资源路径生成Class对象
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return classList;
    }

    /**
     * 根据资源路径获取Class信息
     * @param packageName  包名
     * @param filePath  资源路径
     * @param classList  存放class集合对象
     */
    private void findAndAddClassesInPackageByFile(String packageName, String filePath, List<Class> classList) {
        File dir = new File(filePath);
        //选出文件夹下面所有.class的文件
        File[] files = dir.listFiles(new FileFilter() {
            public boolean accept(File file) {
                return (file.isDirectory() || file.getName().endsWith(".class"));
            }
        });
        for (File file : files) {  //循环文件信息
            if (file.isDirectory()) {  //如果是目录，继续根据文件路径获取文件信息
                findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), classList);
            } else {
                //获取文件名称，去掉文件的后缀 .class
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    //根据包名+文件名创建对应的calss对象，并添加到class集合对象中
                    classList.add(Class.forName(packageName + "." + className));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 循环Class集合信息
     * @param classList
     */
    private void findClassIsAddedCostomAnnotation(List<Class> classList) {
        try{
            for (Class aClass : classList) {
                //初始化Class对象，并放入到map集合中
                classToObjectIntoBeans(aClass, classList);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 设置Bean对象和key之间对应关系，并放入到map集合中
     * @param aClass  接口对象类
     * @param classList  所有Class集合
     */
    private void classToObjectIntoBeans(Class aClass, List<Class> classList) {
        Object obj = null;
        //service对象
        Service annotation = null;
        try{
            if(aClass.isInterface()){  //是否为接口Class，为接口进入找到对应的实现类
                for(Class<?> implClass : classList) {  //循环Class集合信息
                    if (implClass.isInterface()) {  //是否为接口
                        continue;
                    }
//                    Class<?> fieldClassCopy = implClass.getClassLoader().loadClass(aClass.getName());
                    annotation = implClass.getAnnotation(Service.class);
                    if (aClass.isAssignableFrom(implClass)) {   //判断aClass是否为当前implClass的超接口,即implClass为aClass的实现类
                        Constructor[] constructors = implClass.getConstructors();  //获取implClass的构造方法
                        for(Constructor constructor : constructors){
                            int parameterCount = constructor.getParameterCount();
                            if(parameterCount==0){
                                obj = constructor.newInstance();   //获取implClass的对象
                            }
                        }
                        break;
                    }
                }
            } else {   //不为接口Class进入
                Constructor[] constructors = aClass.getConstructors();
                for(Constructor constructor : constructors){
                    int parameterCount = constructor.getParameterCount();
                    if(parameterCount==0){
                        obj = constructor.newInstance();
                    }
                }
            }
            if (obj != null) {
                if(annotation == null || StringUtils.isEmpty(annotation.value())){   //判断是否有Service注解
                    beans.put(aClass.getSimpleName(), obj);
                }else{
                    //如果有@Service注解，并且有value值，key值用value值代替
                    beans.put(annotation.value(), obj);
                }

            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 根据名称获取Bean对象
     * @param beanName
     * @param scanContext
     * @return
     */
    public Object getBean(String beanName, ScanContext scanContext) {

        Object beanObject = beans.get(beanName);
        try{
            referenceBindObject(beanObject);
        }catch (Exception e){
            e.printStackTrace();
        }

        Class aClass = beanObject.getClass();
        Annotation annotation = aClass.getAnnotation(Transactional.class);
        if (annotation != null) {
            ProxyFactory proxyFactory = (ProxyFactory) scanContext.getBean("ProxyFactory", scanContext);
            beanObject = proxyFactory.getJdkProxy(beanObject);
        }
        return beanObject;
    }

    /**
     * 绑定对象关系
     * @param beanObject
     * @return
     */
    private Object referenceBindObject(Object beanObject) {
        Class beanClass = beanObject.getClass();
        Field[] declaredFields = beanClass.getDeclaredFields();
        try {
            for (Field field : declaredFields) {
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                Autowired filedAnnotation = field.getAnnotation(Autowired.class);
                if (filedAnnotation == null) {
                    break;
                }
                Class fieldClass = field.getType();
                String classSimpleName = fieldClass.getSimpleName();
                String fieldName = field.getName();
                Object fieldObject = beans.get(classSimpleName);
                if(fieldObject == null){   //如果根据类型查找不到再根据字段名称进行查找
                    fieldObject = beans.get(fieldName);
                }
                Object object = referenceBindObject(fieldObject);
                field.set(beanObject, object);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return beanObject;
    }
}
